﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using Library.Interface;
using System.Diagnostics;
using System.Collections;
using Library.Definition;

namespace Library
{
    public class BallType
    {
        private IDictionary<string, string> battingAidedBowlingComments;
        private IDictionary<string, string>[] battingAidedBowlingCommentsAll;

        private IList<string> bowlingComments;
        private IList<string>[] bowlingCommentsAll;

        internal IList<string> bowlingFeatureDefinition;
        internal IList<string>[] bowlingFeatureDefinitionAll;
        
        private ISplitting splitter;

        public BallType(BowlingFeatures bowlingFeature)
        {
            battingAidedBowlingComments = GetBattingAidedBowlingDictionary(bowlingFeature);
            bowlingComments = GetBowlingFeature(bowlingFeature);
            bowlingFeatureDefinition = GetBowlingFeatureDefinition(bowlingFeature);
            splitter = new TruncatedSplitting();
        }

        public BallType()
        {
            battingAidedBowlingCommentsAll = new Dictionary<string, string>[10];
            bowlingCommentsAll = new List<string>[10];
            bowlingFeatureDefinitionAll = new List<string>[10];
            for (var feature = BowlingFeatures.Length; feature <= BowlingFeatures.BatsmanPlay; feature++)
            {
                int i = (int)feature;
                if (feature <= BowlingFeatures.Line)
                {
                    battingAidedBowlingCommentsAll[i] = GetBattingAidedBowlingDictionary(feature);                    
                }

                bowlingCommentsAll[i] = GetBowlingFeature(feature);
                bowlingFeatureDefinitionAll[i] = GetBowlingFeatureDefinition(feature);
            }

            splitter = new TruncatedSplitting();
        }

        internal static IList<string> GetFeatureList(string FeatureType)
        {
            ISplitting split = new TruncatedSplitting();
            var assembly = Assembly.GetExecutingAssembly();
            var resourceStream = assembly.GetManifestResourceStream(string.Concat("Library.", FeatureType));
            var resources = assembly.GetManifestResourceNames();
            StreamReader streamRead = new StreamReader(resourceStream);
            IList<string> ballFeatures = split.SplitandTruncateStrings(streamRead.ReadToEnd());
            streamRead.Close();
            return ballFeatures;
        }

        private IList<string> GetBowlingFeature(BowlingFeatures bowlingFeature)
        {
            var featureText = bowlingFeature.ToString();
            var assemblyResourcePath = "BallFeaturesText." + featureText + "Features" + "." + featureText + "Features.txt";
            return GetFeatureList(assemblyResourcePath);
        }

        private IList<string> GetBowlingFeatureDefinition(BowlingFeatures bowlingFeature)
        {
            var featureText = bowlingFeature.ToString();
            var assemblyResourcePath = "BallFeaturesText." + featureText + "Features" + "." + "FeatureDefinition.txt";
            return GetFeatureList(assemblyResourcePath);
        }
       
        /// <summary>
        /// Returns an IEnumerable of KeyValuePairs corresponding to a BattingAidedBowlingFeature
        /// </summary>
        /// <param name="bowlingFeature">
        /// The bowlingFeatureType
        /// </param>
        /// <returns>
        /// An IEnumerable of KeyValue pairs showing link between Batting Commentary to Bowling Commentary
        /// </returns>
        private IEnumerable<KeyValuePair<string, string>> GetBattingAidedBowlingFeatures(BowlingFeatures bowlingFeature)
        {
            var featureText = bowlingFeature.ToString();
            var assemblyResourcePath = "BallFeaturesText." + featureText + "Features" + "." + "BattingAided" + featureText + "Features.txt";
            var featureList = GetFeatureList(assemblyResourcePath);
            foreach (var feature in featureList)
            {                
                yield return new KeyValuePair<string, string>(feature.Substring(0, feature.IndexOf(' ')), feature.Substring(feature.IndexOf(' ') + 1));
            }
        }

        private IEnumerable<KeyValuePair<string, string>> GetBattingAidedLengthFeatures()
        {
            var featureList = GetFeatureList("BallFeaturesText.LengthFeatures.BattingAidedLengthFeatures.txt");
            foreach (var feature in featureList)
            {
                yield return new KeyValuePair<string, string>(feature.Substring(0, feature.IndexOf(' ')), feature.Substring(feature.IndexOf(' ') + 1));
            }
        }

        /// <summary>
        /// Classifies inputFile into Length Paramters and returns BattingAidedBowlingFeatures, NormalLengthFeatures, NoLengthInfo
        /// </summary>
        /// <param name="inFileName">
        /// InputFile Name
        /// </param>
        /// <param name="outFileName">
        /// OutputFile Name
        /// </param>
        /// <returns>
        /// Returns a tuple containing Number of BattingAidedBowlingFeatures, NormalLengthFeatures, NoLengthInfo
        /// </returns>
        public Tuple<int, int, int, IList<int> > OutputBowlingBasedReport(string inFileName, BowlingFeatures bowlingFeature, bool writeToFile, bool relationalDependence)
        {   
            int numberBattingAidedComments = 0;
            int numberBowlingCommentsUsuallyObtained = 0;
            int noFeatureInfo = 0;
            IList<int> featureSpecificationList = new int[10];
            
            try
            {                
                var writeString = string.Empty;                        
                var fileLines = File.ReadAllLines(inFileName);                

                foreach(var fileLine in fileLines)
                {
                    var words = splitter.WhiteSpaceBasedSplitting(fileLine);
                    int wordsLength = words.Count;

                    int featureSpecification = CheckBowlingComments(bowlingComments, words);

                    int battingAidedFeatureSpecification = CheckBattingAidedBowlingComments(battingAidedBowlingComments, featureSpecification, words);
                    var formattedLine = fileLine.Substring(fileLine.IndexOf(' '));

                    if (battingAidedFeatureSpecification != -1)
                    {
                        numberBattingAidedComments++;
                        featureSpecificationList[battingAidedFeatureSpecification]++;
                        if (relationalDependence)
                        {
                            string featureType = GetFeatureType(battingAidedFeatureSpecification) + "\n";
                            if (formattedLine.IndexOf("four", StringComparison.OrdinalIgnoreCase) > 0 || formattedLine.IndexOf("4", StringComparison.OrdinalIgnoreCase) > 0 || formattedLine.IndexOf("six", StringComparison.OrdinalIgnoreCase) > 0 || formattedLine.IndexOf("6", StringComparison.OrdinalIgnoreCase) > 0)
                            {
                                featureType = "Boundary " + featureType;
                            }

                            string outFileName = Path.Combine(Path.GetDirectoryName(inFileName), Path.GetFileNameWithoutExtension(inFileName) + "_" + bowlingFeature + "_transition.txt");
                            File.AppendAllText(outFileName, featureType);
                        }
                    }
                    else
                    {
                        if (featureSpecification == -1)
                        {
                            noFeatureInfo++;
                            if (relationalDependence)
                            {
                                string featureType = "No Info\n";
                                
                                if (formattedLine.IndexOf("four", StringComparison.OrdinalIgnoreCase) > 0 || formattedLine.IndexOf("4", StringComparison.OrdinalIgnoreCase) > 0 || formattedLine.IndexOf("six", StringComparison.OrdinalIgnoreCase) > 0 || formattedLine.IndexOf("6", StringComparison.OrdinalIgnoreCase) > 0)
                                {
                                    featureType = "Boundary " + featureType;
                                }

                                string outFileName = Path.Combine(Path.GetDirectoryName(inFileName), Path.GetFileNameWithoutExtension(inFileName) + "_" + bowlingFeature + "_transition.txt");
                                File.AppendAllText(outFileName, featureType);
                            }
                        }
                        else
                        {                            
                            numberBowlingCommentsUsuallyObtained++;
                            if (relationalDependence)
                            { 
                                string featureType = GetFeatureType(featureSpecification) + "\n";
                                if (formattedLine.IndexOf("four", StringComparison.OrdinalIgnoreCase) > 0 || formattedLine.IndexOf("4", StringComparison.OrdinalIgnoreCase) > 0 || formattedLine.IndexOf("six", StringComparison.OrdinalIgnoreCase) > 0 || formattedLine.IndexOf("6", StringComparison.OrdinalIgnoreCase) > 0)
                                {
                                    featureType = "Boundary " + featureType;
                                }

                                string outFileName = Path.Combine(Path.GetDirectoryName(inFileName), Path.GetFileNameWithoutExtension(inFileName) + "_" + bowlingFeature + "_transition.txt");
                                File.AppendAllText(outFileName, featureType);
                            }
                            if (writeToFile)
                            {
                                string featureType = GetFeatureType(featureSpecification);
                                File.AppendAllText(@"C:\Users\Rishi\Documents\BTP\" + featureType + ".txt", fileLine + "\n");
                            }
                            featureSpecificationList[featureSpecification]++;                           
                        }                        
                    }                    
                }                                

                return new Tuple<int, int, int, IList<int> >(numberBattingAidedComments, numberBowlingCommentsUsuallyObtained, noFeatureInfo, featureSpecificationList);
            }

            catch (FileNotFoundException)
            {
                Trace.WriteLine("File not found");
                return new Tuple<int, int, int, IList<int> >(numberBattingAidedComments, numberBowlingCommentsUsuallyObtained, noFeatureInfo, featureSpecificationList);
            }
        }

        public void OutputBowlingBasedReport(string inFileName, bool writeToFile)
        {            
            try
            {
                var outFileName = Path.Combine(Path.GetDirectoryName(inFileName), Path.GetFileNameWithoutExtension(inFileName) + "_out.txt");
                var sw = new StreamWriter(outFileName);
                var writeString = string.Empty;
                var fileLines = File.ReadAllLines(inFileName);

                foreach (var fileLine in fileLines)
                {
                    if (string.IsNullOrWhiteSpace(fileLine))
                    {
                        continue;
                    }
                    var words = splitter.WhiteSpaceBasedSplitting(fileLine);
                    int wordsLength = words.Count;

                    for (var feature = BowlingFeatures.Length; feature <= BowlingFeatures.BatsmanPlay; feature++)
                    {                        
                        int featureSpecification = CheckBowlingComments(bowlingCommentsAll[(int)feature], words);

                        int battingAidedFeatureSpecification = -1;
                        if (feature <= BowlingFeatures.Line)
                        {
                            battingAidedFeatureSpecification = CheckBattingAidedBowlingComments(battingAidedBowlingCommentsAll[(int)feature], featureSpecification, words);
                        }

                        string featureType = string.Empty;

                        if (battingAidedFeatureSpecification != -1)
                        {                            
                            featureType = GetFeatureType(feature, battingAidedFeatureSpecification);                            
                        }
                        else
                        {
                            if (featureSpecification == -1)
                            {
                                featureType = "No Info";
                            }
                            else
                            {
                                featureType = GetFeatureType(feature, featureSpecification);
                            }
                            if (writeToFile)
                            {
                                //featureType = GetFeatureType(feature, featureSpecification) + "\n";
                                //File.AppendAllText(@"C:\Users\Rishi\Documents\BTP\" + featureType + ".txt", fileLine + "\n");                                
                            }
                                                        
                        }
                        sw.Write(featureType + "\t" );
                    }
                    sw.WriteLine();
                }
                sw.Close();
            }

            catch (FileNotFoundException e)
            {
                throw e;
            }
        }

        private string GetFeatureType(int featureSpecification)
        {
            return bowlingFeatureDefinition[featureSpecification-1];            
        }

        private string GetFeatureType(BowlingFeatures feature, int featureSpecification)
        {
            return bowlingFeatureDefinitionAll[(int)feature][featureSpecification - 1];
        }

        private int CheckBattingAidedBowlingComments(IDictionary<string, string> battingAidedBowlingComments, int bowlingCommentExists, IList<string> words)
        {
            IList<int> featureSpecifications = new int[10];
            
            if (bowlingCommentExists == -1)
            {
                for (int i = 0; i < words.Count; i++)
                {
                    if (battingAidedBowlingComments.ContainsKey(words[i]))
                    {                       
                        int index = Convert.ToInt32(battingAidedBowlingComments[words[i]].Substring(1));
                        BuildFeatureSpecification(featureSpecifications, battingAidedBowlingComments[words[i]][0], index);
                    }
                }
                return FindSpecificationType(featureSpecifications);
            }
            return -1;
        }

        private int CheckBowlingComments(IList<string> bowlingComments, IList<string> words)
        {            
            IList<int> featureSpecification = new int[10];
            
            try
            {
                for (int i = 0; i < words.Count; i++)
                {
                    foreach (var bowlingComment in bowlingComments)
                    {
                        var bowlingCommentSplit = bowlingComment.Split(' ');
                        if (bowlingCommentSplit.Length < 3)
                        {
                            if (string.CompareOrdinal(words[i], bowlingCommentSplit[0]) == 0)
                            {
                                int index = Convert.ToInt32(bowlingCommentSplit[1].Substring(1));
                                BuildFeatureSpecification(featureSpecification, bowlingCommentSplit[1][0], index);
                            }
                        }
                        else
                        {
                            if (string.CompareOrdinal(words[i], bowlingCommentSplit[0]) == 0)
                            {
                                // This is because it can be 'outside off'/ 'outside the off' etc.. where 'outside off' is my bowling comment//
                                if ((i + 1 < words.Count && string.CompareOrdinal(words[i + 1], bowlingCommentSplit[1]) == 0) || (i + 2 < words.Count && string.CompareOrdinal(words[i + 2], bowlingCommentSplit[1]) == 0) || (i + 3 < words.Count && string.CompareOrdinal(words[i + 3], bowlingCommentSplit[1]) == 0))
                                {
                                    int index = Convert.ToInt32(bowlingCommentSplit[2].Substring(1));
                                    BuildFeatureSpecification(featureSpecification, bowlingCommentSplit[2][0], index);
                                }
                            }
                        }

                    }
                }
            }

            catch (InvalidDataException e)
            {
                Trace.WriteLine(e.Message);
                return -1;
            }
            return FindSpecificationType(featureSpecification);
        }

        private int FindSpecificationType(IList<int> featureSpecification)
        {
            int max = 0;
            int specificationType = -1;
            for (int i = 0; i < featureSpecification.Count; i++)
            {
                if (featureSpecification[i] > max)
                {
                    max = featureSpecification[i];
                    specificationType = i;
                }
            }
            return specificationType;
        }

        /// <summary>
        /// According to + or - it increments or decrements the corresponding featureSpecification
        /// </summary>
        /// <param name="featureSpecification"> The List of feature Specifications</param>
        /// <param name="specificationSign"> The bowling Comment </param>
        /// <param name="index"> Index of the specification </param>
        /// <exception cref="InvalidDataException"> Occurs when the string doesn't start with a + or -</exception>
        private void BuildFeatureSpecification(IList<int> featureSpecification, char specificationSign, int index)
        {
            switch (specificationSign)
            {
                case '+':
                    {
                        featureSpecification[index]++;
                        break;
                    }
                case '-':
                    {
                        featureSpecification[index]--;
                        break;
                    }
                default:
                    {
                        throw new InvalidDataException("Should only be + or -");
                    }
            }
        }

        private IDictionary<string, string> GetBattingAidedBowlingDictionary(BowlingFeatures bowlingFeature)
        {
            IDictionary<string, string> dictionary = new Dictionary<string, string>();
            foreach (var features in GetBattingAidedBowlingFeatures(bowlingFeature))
            {
                dictionary.Add(features.Key, features.Value);
            }

            return dictionary;
        }
    }
}
